﻿#if UNITY_EDITOR
using Devil;
using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;

namespace GameToolkit
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class DefaultSerializableFieldAttribute : Attribute
    {
        public Type FieldType { get; private set; }
        public DefaultSerializableFieldAttribute(Type type) { FieldType = type; }
    }

    sealed public partial class JsonSerializedObject
    {

        #region serialization fields

        // 序列化成员
        internal interface ISerializableField
        {
            string Name { get; }
            string FieldType { get; }
            bool IsArray { get; }
            bool IsCloneable { get; }
            void SetValue(SerializationContext context, object target, JsonData data);
            JsonData GetValue(SerializationContext context, object target);
            void Clone(SerializationContext context, object from, object to);
        }

        internal abstract class DefaultField : ISerializableField
        {
            protected readonly FieldInfo mField;
            protected readonly Type mDataType;
            protected readonly bool mIsArray;
            bool mCloneable;
            public bool IsCloneable => mCloneable;
            public string Name => mField.Name;
            public bool IsArray => mIsArray;
            public virtual string FieldType => mIsArray ? $"{mDataType.Name}[]" : mDataType.Name;
            internal DefaultField(FieldInfo finfo)
            {
                mField = finfo;
                mDataType = TypeWrapper.GetArrayElementType(finfo.FieldType);
                if (mDataType == null)
                {
                    mIsArray = false;
                    mDataType = finfo.FieldType;
                }
                else
                {
                    mIsArray = true;
                }
                var att = finfo.GetCustomAttribute<JsonCloneableAttribute>();
                if (att != null && !att.IsCloneable)
                {
                    mCloneable = false;
                }
                else
                {
                    att = mDataType.GetCustomAttribute<JsonCloneableAttribute>();
                    mCloneable = att == null || att.IsCloneable;
                }
            }

            public void SetValue(SerializationContext context, object target, JsonData data)
            {
                if (mIsArray)
                {
                    if (data != null && !data.IsArray)
                        throw new InvalidOperationException($"invalid input data for field {mField.DeclaringType.Name}.{mField.Name}\ndata: {data.ToJson()}");
                    var size = data == null ? 0 : data.Count;
                    var arr = mField.GetValue(target) as IList;
                    if (arr == null || arr.Count != size)
                    {
                        if (mField.FieldType.IsArray)
                        {
                            arr = Array.CreateInstance(mDataType, size);
                        }
                        else
                        {
                            var constructor = mField.FieldType.GetConstructor(new Type[] { typeof(int) });
                            arr = constructor.Invoke(new object[] { size }) as IList;
                        }
                    }
                    for (int i = 0; i < size; i++)
                    {
                        if (arr.Count > i)
                            arr[i] = ToObject(context, data[i]);
                        else
                            arr.Add(ToObject(context, data[i]));
                    }
                    mField.SetValue(target, arr);
                }
                else
                {
                    var inst = ToObject(context, data);
                    mField.SetValue(target, inst);
                }
            }

            public JsonData GetValue(SerializationContext context, object target)
            {
                if (mIsArray)
                {
                    var arr = mField.GetValue(target) as IList;
                    var data = new JsonData(JsonType.Array);
                    var size = arr == null ? 0 : arr.Count;
                    for (int i = 0; i < size; i++)
                    {
                        data.Add(ToJson(context, arr[i]));
                    }
                    return data;
                }
                else
                {
                    var obj = mField.GetValue(target);
                    var data = ToJson(context, obj);
                    return data;
                }
            }

            public void Clone(SerializationContext context, object fromTarget, object toTarget)
            {
                if (!mCloneable && context.IsCloneableFieldsOnly)
                    return;
                if (mIsArray)
                {
                    var arr = mField.GetValue(fromTarget) as IList;
                    var size = arr == null ? 0 : arr.Count;
                    var dest = mField.GetValue(toTarget) as IList;
                    if (dest == null || dest.Count != size)
                    {
                        if (mField.FieldType.IsArray)
                        {
                            dest = Array.CreateInstance(mDataType, size);
                        }
                        else
                        {
                            var constructor = mField.FieldType.GetConstructor(new Type[] { typeof(int) });
                            arr = constructor.Invoke(new object[] { size }) as IList;
                        }
                    }
                    for (int i = 0; i < size; i++)
                    {
                        if (dest.Count > i)
                            dest[i] = Clone(context, arr[i]);
                        else
                            dest.Add(Clone(context, arr[i]));
                    }
                    mField.SetValue(toTarget, dest);
                }
                else
                {
                    var obj = mField.GetValue(fromTarget);
                    var dest = Clone(context, obj);
                    mField.SetValue(toTarget, dest);
                }
            }

            protected abstract object ToObject(SerializationContext context, JsonData data);
            protected abstract JsonData ToJson(SerializationContext context, object obj);
            protected abstract object Clone(SerializationContext context, object obj);
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(int))]
        internal class IntField : DefaultField
        {

            internal IntField(FieldInfo finfo) : base(finfo) { }

            protected override object ToObject(SerializationContext context, JsonData data)
            {
                return (int)data;
            }
            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                var v = (int)obj;
                return v;
            }

            protected override object Clone(SerializationContext context, object obj)
            {
                return (int)obj;
            }
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(uint))]
        internal class UIntField : DefaultField
        { 
            internal UIntField(FieldInfo finfo) : base(finfo) { }
            protected override object ToObject(SerializationContext context, JsonData data)
            {
                return (uint)data;
            }
            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                var v = (uint)obj;
                return v;
            }
            protected override object Clone(SerializationContext context, object obj)
            {
                return (uint)obj;
            }
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(long))]
        internal class LongField : DefaultField
        {
            internal LongField(FieldInfo finfo) : base(finfo) { }

            protected override object ToObject(SerializationContext context, JsonData data)
            {
                return (long)data;
            }
            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                var v = (long)obj;
                return v;
            }

            protected override object Clone(SerializationContext context, object obj)
            {
                return (long)obj;
            }
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(float))]
        internal class FloatField : DefaultField
        {
            internal FloatField(FieldInfo finfo) : base(finfo) { }

            protected override object ToObject(SerializationContext context, JsonData data)
            {
                return (float)data;
            }
            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                var v = (float)obj;
                return v;
            }
            protected override object Clone(SerializationContext context, object obj)
            {
                return (float)obj;
            }
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(double))]
        internal class DoubleField : DefaultField
        {
            internal DoubleField(FieldInfo finfo) : base(finfo) { }

            protected override object ToObject(SerializationContext context, JsonData data)
            {
                return (double)data;
            }
            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                var v = (double)obj;
                return v;
            }
            protected override object Clone(SerializationContext context, object obj)
            {
                return (double)obj;
            }
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(bool))]
        internal class BooleanField : DefaultField
        {
            internal BooleanField(FieldInfo finfo) : base(finfo) { }

            protected override object ToObject(SerializationContext context, JsonData data)
            {
                return (bool)data;
            }
            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                var v = (bool)obj;
                return v;
            }
            protected override object Clone(SerializationContext context, object obj)
            {
                return (bool)obj;
            }
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(string))]

        internal class StringField : DefaultField
        {
            internal StringField(FieldInfo finfo) : base(finfo) { }
            protected override object ToObject(SerializationContext context, JsonData data)
            {
                return (string)data;
            }
            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                return obj == null ? null : obj.ToString();
            }
            protected override object Clone(SerializationContext context, object obj)
            {
                return obj == null ? null : obj.ToString();
            }
        }

        // 定义默认构造方法
        [DefaultSerializableField(typeof(Vector2))]
        [DefaultSerializableField(typeof(Vector3))]
        [DefaultSerializableField(typeof(Vector4))]
        [DefaultSerializableField(typeof(Color))]
        [DefaultSerializableField(typeof(Quaternion))]
        internal class VectorField : DefaultField
        {
            public VectorField(FieldInfo finfo) : base(finfo)
            {
            }

            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                var dt = new JsonData(JsonType.Array);
                if (obj is Vector2 v2)
                {
                    dt.Add(v2.x);
                    dt.Add(v2.y);
                }
                else if (obj is Vector3 v3)
                {
                    dt.Add(v3.x);
                    dt.Add(v3.y);
                    dt.Add(v3.z);
                }
                else if (obj is Vector4 v4)
                {
                    dt.Add(v4.x);
                    dt.Add(v4.y);
                    dt.Add(v4.z);
                    dt.Add(v4.w);
                }
                else if (obj is Color c4)
                {
                    dt.Add(c4.r);
                    dt.Add(c4.g);
                    dt.Add(c4.b);
                    dt.Add(c4.a);
                }
                else if (obj is Quaternion q4)
                {
                    dt.Add(q4.x);
                    dt.Add(q4.y);
                    dt.Add(q4.z);
                    dt.Add(q4.w);
                }
                return dt;
            }

            protected override object ToObject(SerializationContext context, JsonData data)
            {
                if (data == null || !data.IsArray)
                    return Activator.CreateInstance(mDataType);
                var size = data.Count;
                if (mDataType == typeof(Vector2))
                {
                    Vector2 v;
                    v.x = size < 1 ? 0 : (float)data[0];
                    v.y = size < 2 ? 0 : (float)data[1];
                    return v;
                }
                else if (mDataType == typeof(Vector3))
                {
                    Vector3 v;
                    v.x = size < 1 ? 0 : (float)data[0];
                    v.y = size < 2 ? 0 : (float)data[1];
                    v.z = size < 3 ? 0 : (float)data[2];
                    return v;
                }
                else if (mDataType == typeof(Vector4))
                {
                    Vector4 v;
                    v.x = size < 1 ? 0 : (float)data[0];
                    v.y = size < 2 ? 0 : (float)data[1];
                    v.z = size < 3 ? 0 : (float)data[2];
                    v.w = size < 4 ? 0 : (float)data[3];
                    return v;
                }
                else if (mDataType == typeof(Color))
                {
                    Color v;
                    v.r = size < 1 ? 0 : (float)data[0];
                    v.g = size < 2 ? 0 : (float)data[1];
                    v.b = size < 3 ? 0 : (float)data[2];
                    v.a = size < 4 ? 0 : (float)data[3];
                    return v;
                }
                else if (mDataType == typeof(Quaternion))
                {
                    Quaternion v;
                    v.x = size < 1 ? 0 : (float)data[0];
                    v.y = size < 2 ? 0 : (float)data[1];
                    v.z = size < 3 ? 0 : (float)data[2];
                    v.w = size < 4 ? 0 : (float)data[3];
                    return v;
                }
                return Activator.CreateInstance(mDataType);
            }

            protected override object Clone(SerializationContext context, object obj)
            {
                if (obj is Vector2 v2)
                    return v2;
                else if (obj is Vector3 v3)
                    return v3;
                else if (obj is Vector4 v4)
                    return v4;
                else if (obj is Color c)
                    return c;
                else if (obj is Quaternion q)
                    return q;
                else
                    return Activator.CreateInstance(mDataType);
            }
        }

        internal class EnumField : DefaultField
        {
            internal EnumField(FieldInfo finfo) : base(finfo) { }

            public override string FieldType => mIsArray ? $"Enum<{mDataType.Name}>[]" : $"Enum<{mDataType.Name}>";

            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                return Convert.ToInt64(obj);
            }

            protected override object ToObject(SerializationContext context, JsonData data)
            {
                var value = (long)data;
                var type = mDataType.GetEnumUnderlyingType();
                if (type == typeof(int))
                    return Convert.ToInt32(value);
                else if (type == typeof(byte))
                    return Convert.ToByte(value);
                else if (type == typeof(ushort))
                    return Convert.ToUInt16(value);
                else if (type == typeof(uint))
                    return Convert.ToUInt32(value);
                else if (type == typeof(ulong))
                    return Convert.ToUInt64(value);
                else
                    return value;
            }
            protected override object Clone(SerializationContext context, object obj)
            {
                var type = mDataType.GetEnumUnderlyingType();
                if (type == typeof(int))
                    return Convert.ToInt32(obj);
                else if (type == typeof(byte))
                    return Convert.ToByte(obj);
                else if (type == typeof(ushort))
                    return Convert.ToUInt16(obj);
                else if (type == typeof(uint))
                    return Convert.ToUInt32(obj);
                else if (type == typeof(ulong))
                    return Convert.ToUInt64(obj);
                else
                    return obj;
            }
        }

        internal class ReferenceField : DefaultField
        {
            bool mIsUnityAsset;
            public System.Type ReferenceType => mDataType;
            public override string FieldType => mIsArray ? $"Ref<{mDataType.Name}>[]" : $"Ref<{mDataType.Name}>";
            internal ReferenceField(FieldInfo finfo) : base(finfo)
            {
                mIsUnityAsset = TypeWrapper.IsUnityAsset(mDataType);
            }

            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                if (obj == null)
                    return null;
                if (mIsUnityAsset)
                {
                    var uasset = obj as UnityEngine.Object;
                    string guid;
                    long localId;
                    var exists = AssetDatabase.TryGetGUIDAndLocalFileIdentifier(uasset, out guid, out localId);
                    if (exists)
                    {
                        var dt = new JsonData();
                        dt["guid"] = guid;
                        dt["localid"] = Localization.ToString(localId);
                        return dt;
                    }
                    else
                    {
                        Debug.LogWarning($"The asset \"{uasset.name}\" is not saved asset and will be missing while exporting assets to json.");
                        return null;
                    }
                }
                else
                {
                    return context.GetLocalFileID(obj);
                }
            }
            protected override object ToObject(SerializationContext context, JsonData data)
            {
                object inst = null;
                if (data == null)
                {
                    inst = null;
                }
                else if (mIsUnityAsset)
                {
                    var guid = (string)data["guid"];
                    var localid = (long)data["localid"];
                    var path = AssetDatabase.GUIDToAssetPath(guid);
                    var assets = string.IsNullOrEmpty(path) ? null : AssetDatabase.LoadAllAssetsAtPath(path);
                    var len = assets == null ? 0 : assets.Length;
                    long id;
                    for (int i = 0; i < len; i++)
                    {
                        if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(assets[i], out guid, out id) && id == localid)
                        {
                            inst = assets[i];
                            break;
                        }
                    }
                }
                else if(data.IsLong)
                {
                    inst = context.GetLocalFile((int)data);
                }
                else
                {
                    inst = null;
                }
                return inst;
            }

            protected override object Clone(SerializationContext context, object obj)
            {
                if (mIsUnityAsset)
                    return obj;
                var inst = context.GetClonedInstance(obj);
                if (inst == null && obj != null && (IsCloneable || !context.IsCloneableFieldsOnly))
                {
                    var wrapper = GetWrapper(obj.GetType());
                    wrapper.InitFields();
                    inst = wrapper.Create();
                    context.SetClonedValue(obj, inst);
                    wrapper.Copy(context, obj, inst);
                }
                return inst;
            }
        }

        internal class InstanceField : DefaultField
        {
            TypeWrapper mWrappper;
            internal TypeWrapper Wrapper => mWrappper;
            internal InstanceField(FieldInfo finfo, TypeWrapper wrapper) : base(finfo)
            {
                mWrappper = wrapper;
            }
            protected override object ToObject(SerializationContext context, JsonData data)
            {
                if (data == null)
                    return null;
                mWrappper.InitFields();
                var inst = mWrappper.Create();
                mWrappper.Import(context, inst, data);
                return inst;
            }

            protected override JsonData ToJson(SerializationContext context, object obj)
            {
                mWrappper.InitFields();
                return mWrappper.Export(context, obj);
            }

            protected override object Clone(SerializationContext context, object obj)
            {
                if (!IsCloneable && context.IsCloneableFieldsOnly)
                    return null;
                mWrappper.InitFields();
                var inst = mWrappper.Create();
                context.SetClonedValue(obj, inst);
                mWrappper.Copy(context, obj, inst);
                return inst;
            }
        }


        internal enum ESerializeType
        {
            NotSerializable,
            UnityAsset,
            Reference,
            Serializable,
            UnityAssetArray,
            ReferenceArray,
            SerializableArray,
        }

        public class TypeWrapper
        {
            public string Name { get; private set; }
            public bool IsRecursiveWrapper { get; private set; }
            public Type WrapType => mType;
            System.Type mType;
            ConstructorInfo mConstructor;
            Dictionary<string, ISerializableField> mFields;
            Dictionary<string, string> mOldNames;
            bool mIsInitialized;
            readonly object mLock = new object();

            internal static bool IsUnityAsset(Type type)
            {
                var utype = typeof(UnityEngine.Object);
                var tp = type;
                while (tp != null)
                {
                    if (tp == utype)
                        return true;
                    tp = tp.BaseType;
                }
                return false;
            }

            internal static Type GetArrayElementType(Type type)
            {
                if (type.IsArray)
                    return type.GetElementType();
                if (type.IsGenericType)
                {
                    if (type.Namespace == "System.Collections.Generic" && type.Name == "List`1")
                        return type.GetGenericArguments()[0];
                }
                return null;
            }

            internal static ESerializeType GetSerializableType(Type type/*, Dictionary<Type, FieldConstructor> defaultSerializable*/)
            {
                if (type.IsEnum)
                    return ESerializeType.Serializable;
                if (type.GetCustomAttribute<NonSerializedAttribute>() != null)
                    return ESerializeType.NotSerializable;
                ESerializeType sertype;
                if (IsUnityAsset(type))
                    sertype = ESerializeType.UnityAsset;
                else if (type.GetCustomAttribute<SerializableAttribute>() != null || sDefaultFields.ContainsKey(type))
                    sertype = ESerializeType.Serializable;
                else
                    sertype = ESerializeType.NotSerializable;
                return sertype;
            }

            internal static ESerializeType GetSerializableType(FieldInfo field, /*Dictionary<Type, FieldConstructor> defaultSerializable,*/ out Type serializeType)
            {
                serializeType = field.FieldType;
                if (field.GetCustomAttribute<NonSerializedAttribute>() != null)
                    return ESerializeType.NotSerializable;
                var type = GetArrayElementType(serializeType);
                var isArray = type != null;
                if (type != null)
                    serializeType = type;
                ESerializeType sertype = GetSerializableType(serializeType/*, defaultSerializable*/);
                if (sertype != ESerializeType.NotSerializable)
                {
                    if (field.GetCustomAttribute<SerializeReference>() != null)
                        sertype = ESerializeType.Reference;
                    else if (field.GetCustomAttribute<SerializeField>() == null && !field.IsPublic)
                        sertype = ESerializeType.NotSerializable;

                    if (sertype == ESerializeType.Reference && serializeType.IsValueType)
                    {
                        sertype = ESerializeType.Serializable;
                        Debug.LogError($"Value type<{serializeType.Name}> cannot be serialize as reference({field.DeclaringType.Name}.{field.Name}).");
                    }

                    if (isArray)
                    {
                        if (sertype == ESerializeType.Reference)
                            sertype = ESerializeType.ReferenceArray;
                        else if (sertype == ESerializeType.UnityAsset)
                            sertype = ESerializeType.UnityAssetArray;
                        else if (sertype == ESerializeType.Serializable)
                            sertype = ESerializeType.SerializableArray;
                    }
                }
                return sertype;
            }

            internal static ConstructorInfo GetConstructor(Type type)
            {
                var cons = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                for (int i = 0; i < cons.Length; i++)
                {
                    var constructor = cons[i];
                    var p = constructor.GetParameters();
                    if (p == null || p.Length == 0)
                        return constructor;
                }
                return null;
            }

            internal static ConstructorInfo GetFieldConstructor(Type type)
            {
                var cons = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                for (int i = 0; i < cons.Length; i++)
                {
                    var constructor = cons[i];
                    var p = constructor.GetParameters();
                    if (p != null && p.Length == 1 && p[0].ParameterType == typeof(FieldInfo))
                        return constructor;
                }
                return null;
            }

            internal TypeWrapper(Type type, ConstructorInfo constructor)
            {
                mType = type;
                mConstructor = constructor;
                Name = type.FullName;
            }

            internal void Alias(string name)
            {
                Name = name;
            }

            internal void InitFields(/*Dictionary<Type, FieldConstructor> defaultFields, Dictionary<Type, TypeWrapper> wrappersCache*/)
            {
                lock (mLock)
                {
                    if (mIsInitialized)
                        return;
                    mIsInitialized = true;
                    mOldNames = new Dictionary<string, string>();
                    mFields = new Dictionary<string, ISerializableField>();
                    var flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
                    var tp = mType;
                    while(tp != null)
                    {
                        var fields = tp.GetFields(flags);
                        ConstructorInfo sfield;
                        for (int i = 0; i < fields.Length; i++)
                        {
                            var field = fields[i];
                            Type type;
                            var sertype = GetSerializableType(field, /*defaultFields,*/ out type);
                            if (sertype == ESerializeType.NotSerializable)
                                continue;
                            ISerializableField serializer = null;
                            TypeWrapper wrapper = null;
                            sWrappers.TryGetValue(type.FullName, out wrapper);
                            if (type.IsEnum)
                            {
                                serializer = new EnumField(field);
                            }
                            else if (sertype == ESerializeType.Reference || sertype == ESerializeType.ReferenceArray 
                                || sertype == ESerializeType.UnityAsset || sertype == ESerializeType.UnityAssetArray)
                            {
                                serializer = new ReferenceField(field);
                            }
                            else if (!sDefaultFields.TryGetValue(type, out sfield))
                            {
                                if (wrapper != null)
                                {
                                    serializer = new InstanceField(field, wrapper);
                                }
                                else if (!type.IsValueType)
                                {
                                    serializer = new ReferenceField(field);
                                }
                            }
                            else
                            {
                                serializer = sfield.Invoke(new object[] { field }) as ISerializableField;
                            }
                            if (serializer != null)
                            {
                                mFields[field.Name] = serializer;
                                var formerly = field.GetCustomAttributes<FormerlySerializedAsAttribute>();
                                if (formerly != null)
                                {
                                    foreach (var att in formerly)
                                    {
                                        mOldNames[att.oldName] = field.Name;
                                    }
                                }
                            }
                        }
                        tp = tp.BaseType;
                    }

                    IsRecursiveWrapper = false;
                }
            }

            public object Create()
            {
                if (mConstructor == null)
                    return Activator.CreateInstance(mType);
                else
                    return mConstructor.Invoke(NONE_ARGS);
            }

            public void Import(SerializationContext context, object target, JsonData data)
            {
                if (target == null || data == null)
                    return;
                if (!data.IsObject)
                    throw new InvalidOperationException($"invalid input data for wrap type<{mType.Name}>\ndata: {data.ToJson()}");
                string fieldName;
                foreach (var key in data.Keys)
                {
                    ISerializableField field;
                    if (!mFields.TryGetValue(key, out field) && mOldNames.TryGetValue(key, out fieldName))
                    {
                        mFields.TryGetValue(fieldName, out field);
                    }
                    if (field != null)
                        field.SetValue(context, target, data[key]);
                }
            }

            public JsonData Export(SerializationContext context, object target)
            {
                if (target == null)
                    return null;
                var obj = new JsonData(JsonType.Object);
                foreach (var kv in mFields)
                {
                    var dt = kv.Value.GetValue(context, target);
                    obj[kv.Key] = dt;
                }
                return obj;
            }


            internal void Copy(SerializationContext context, object from, object to)
            {
                if (from == null || to == null)
                    return;
                foreach (var field in mFields.Values)
                {
                    field.Clone(context, from, to);
                }
            }

            void AppendSerializeStructure(StringBuilder buf, int indent, bool asArray)
            {
                buf.Append("SerializableObject<").Append(mType.FullName).Append(">");
                if (asArray)
                    buf.Append("[]");
                buf.Append("\n");
                buf.AppendTab(indent).Append("{\n");
                InitFields();
                foreach (var field in mFields.Values)
                {
                    buf.AppendTab(indent + 1).Append(field.Name).Append(" :");
                    if (field is InstanceField insf)
                    {
                        insf.Wrapper.AppendSerializeStructure(buf, indent + 1, field.IsArray);
                    }
                    else
                    {
                        buf.Append(field.FieldType);
                    }
                    buf.Append('\n');
                }

                buf.AppendTab(indent).Append("}");
            }

            public override string ToString()
            {
                var buf = ParallelUtils.GetBuilder();
                AppendSerializeStructure(buf, 0, false);
                return ParallelUtils.ReturnRelease(buf);
            }
        }

        #endregion

    }
}

#endif